﻿using AllDemo.Tools;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;

namespace AllDemo.TcpCommunication
{
    public class ZTcpServer
    {
        //保存客户端IP和socket示例字典
        public Dictionary<string, Socket> clientSocketDic;
        //保存客户端IP和最后一次发送数据时间
        public Dictionary<string, long> clientTimeOutDic;
        //保存客户端线程
        public Dictionary<string, Thread> clientThreadDic;
        //在将客户端加入集合的时候是否移除端口
        private bool isRemoveClientPort = true;

        private Socket serverSocket;

        public bool IsRemoveClientPort
        {
            get { return isRemoveClientPort; }
            set { isRemoveClientPort = value; }
        }

        public ZTcpServer()
        {

            clientSocketDic = new Dictionary<string, Socket>();
            clientTimeOutDic = new Dictionary<string, long>();
            clientThreadDic = new Dictionary<string, Thread>();

        }
        public int createConnection(string sIp, int sPort)
        {
            try
            {
                //第一个参数：设置寻址协议为ipv4
                //第二个参数：设置数据传输方式
                //第三个参数：设置通信协议
                //创建服务器端Socket
                serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                Console.WriteLine("开始创建服务端Socket服务！");
                IPAddress ip = IPAddress.Parse(sIp);

                IPEndPoint ipEndpoint = new IPEndPoint(ip, sPort);
                //绑定IP和端口
                serverSocket.Bind(ipEndpoint);
                //开启侦听
                //挂起连接列队的最大长度
                serverSocket.Listen(1000);
                //设置线程激活时的回调方法
                ThreadPool.QueueUserWorkItem(new WaitCallback(this.StartAcceptClient), serverSocket);
                return 0;
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
                return -1;
                throw ex;
            }
        }


        //监听客户端连接
        public void StartAcceptClient(object state)
        {

            var serverSocket = (Socket)state;
            while (true)
            {
                try
                {
                    //为新建连接创建新的Socket对象，如果没有新的连接进入该线程会阻塞
                    Socket proxSocket = serverSocket.Accept();
                    string IPStr = proxSocket.RemoteEndPoint.ToString();
                    //加入集合时是否移除端口
                    if (this.IsRemoveClientPort)
                    {
                        Console.WriteLine("客户端IP：" + IPStr);
                        IPStr = IPStr.Split(':')[0];
                        //移除三个字典内的同键项
                        clearDic(IPStr, false);
                        //客户端socket加入集合
                        clientSocketDic.Add(IPStr, proxSocket);
                        //客户端接入时间加入集合
                        clientTimeOutDic.Add(IPStr, DateTool.getNowMillisecond());
                    }
                    Thread clientThread = new Thread(RecieveData);
                    clientThread.Start(proxSocket);
                    clientThreadDic.Add(IPStr, clientThread);
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.ToString());
                    throw ex;
                    // throw ex;
                }
            }

        }


        public event Action<string, string> GetClientMsg;
        //接收数据
        public void RecieveData(object obj)
        {
            var proxSocket = (Socket)obj;
            byte[] data = new byte[1024 * 1024];
            int reallen = 0;
            //方法返回值，实际接收的数据的长度
            try     //处理客户端异常退出
            {
                while (true)
                {
                    try
                    {
                        //接收客户端数据
                        reallen = proxSocket.Receive(data, 0, data.Length, SocketFlags.None);

                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine(ex);
                        Console.WriteLine("关闭自己的线程。");
                        Thread.CurrentThread.Abort();
                    }
                    //获取IP和端口
                    string IPStr = proxSocket.RemoteEndPoint.ToString();
                    //如果字典中加入的时去掉端口的IP
                    if (this.IsRemoveClientPort)
                    {
                        //获取IP
                        IPStr = IPStr.Split(':')[0];
                        //检查该IP是否在字典中
                        if (this.clientTimeOutDic.ContainsKey(IPStr))
                        {
                            //如果在集合中将时间更新为当前时间
                            this.clientTimeOutDic[IPStr] = DateTool.getNowMillisecond();
                        }
                    }
                    string formClientMsg = Encoding.UTF8.GetString(data, 0, reallen);
                    if (GetClientMsg != null)
                    {
                        GetClientMsg(proxSocket.RemoteEndPoint.ToString(), formClientMsg);   //外部绑定事件，传入参数为IP，和接收到的消息
                    }
                    Console.WriteLine(string.Format("接收客户端{0}的消息：{1}", proxSocket.RemoteEndPoint.ToString(), formClientMsg));
                    if (formClientMsg == "")
                    {
                        Console.WriteLine("客户端：" + IPStr + "的挂起已结束，即将清理字典，杀死线程。");
                        clearDic(IPStr, true);
                    }
                    Thread.Sleep(5);
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
                Console.WriteLine("关闭自己的线程。");
                Thread.CurrentThread.Abort();
                throw ex;
            }
        }



        //发送操作
        public int SendMsgToClient(string IPStr, string msg)
        {
            byte[] msgBytes = Encoding.ASCII.GetBytes(msg);
            int result;
            try
            {
                //发送成功返回0
                result = this.clientSocketDic[IPStr].Send(msgBytes);
                return result;
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
                return -1;
                throw ex;
            }



        }
        public int SendMsgToClient(string IPStr, byte[] msgBytes)
        {

            int result;
            try
            {
                //发送成功返回0
                result = this.clientSocketDic[IPStr].Send(msgBytes);
                return result;
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
                return -1;
                throw ex;
            }



        }

        //关闭socket
        public void CloseSocket(Socket socket)
        {
            try
            {
                socket.Shutdown(SocketShutdown.Both);
                socket.Close();
            }
            catch (Exception ex)
            {
                throw ex;
            }

        }

        public void clearDic(string IPStr, bool isKillSelf)
        {
            try
            {
                if (this.clientSocketDic.ContainsKey(IPStr))
                {
                    try
                    {
                        CloseSocket(clientSocketDic[IPStr]);
                        this.clientSocketDic.Remove(IPStr);

                    }
                    catch (Exception ex)
                    {

                        Console.WriteLine(ex.ToString());
                    }

                }
                if (this.clientTimeOutDic.ContainsKey(IPStr))
                {
                    try
                    {
                        this.clientTimeOutDic.Remove(IPStr);
                    }
                    catch (Exception ex)
                    {

                        Console.WriteLine(ex.ToString());
                    }

                }
                if (this.clientThreadDic.ContainsKey(IPStr))
                {
                    try
                    {
                        if (isKillSelf)
                        {

                            this.clientThreadDic.Remove(IPStr);
                            Thread.CurrentThread.Abort();

                        }
                        else
                        {
                            this.clientThreadDic[IPStr].Abort();
                            this.clientThreadDic.Remove(IPStr);
                        }

                    }
                    catch (Exception ex)
                    {

                        Console.WriteLine(ex.ToString());
                    }

                }
            }
            catch (Exception ex)
            {

                throw ex;
            }

        }
    }
}
